import os
import shutil
import sys
import tempfile
import threading
import subprocess
import time
import tkinter as tk
import webbrowser
import json
from datetime import datetime, timedelta
from tkinter import filedialog, scrolledtext, messagebox
from open_jg_page_get_cookie import open_jg_page
from read_search_file import get_chrome_download_directory
import requests
from flask_server import start_server
from create_plan_search import start_sub_account_url
from tkinter import ttk

current_version = "1.0.5"
update_url = "http://47.112.119.104/new_zc_kz/version.json"

save_base_data_json = os.path.join("static", "get_jg_data.json")
html_file = os.path.join("static", "generated_excel.html")

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.master.title(f"新界面-创建计划-种草(信息流&搜索推广)-当前版本:{current_version}")
        self.red_dot_visible = False  # 初始为隐藏

        self.temp_dir = tempfile.mkdtemp(prefix="update_")
        # atexit.register(self.cleanup)

        # 设置窗口大小
        window_width = 650
        window_height = 460

        # 获取屏幕尺寸以计算布局，使窗口居中
        screen_width = self.master.winfo_screenwidth()
        screen_height = self.master.winfo_screenheight()

        # 计算居中位置
        x_coordinate = int((screen_width / 2) - (window_width / 2))
        y_coordinate = int((screen_height / 2) - (window_height / 2))

        # 设置窗口位置和大小
        self.master.geometry(f"{window_width}x{window_height}+{x_coordinate}+{y_coordinate}")

        self.pack(padx=10, pady=10)
        self.create_widgets()

        threading.Thread(target=self.check_update_init, daemon=True).start()
        threading.Thread(target=self.monitor_selected_file, daemon=True).start()
        # 存储用户选择的文件路径和并发数
        self.file_path = ""
        self.concurrency = 3

        self.master.protocol("WM_DELETE_WINDOW", self.on_closing)

    def create_widgets(self):
        # 主容器框架
        main_frame = tk.Frame(self)
        main_frame.pack(fill="both", expand=True, padx=10, pady=10)

        # 内容框架 - 包含除底部进度条外的所有内容
        content_frame = tk.Frame(main_frame)
        content_frame.pack(fill="both", expand=True)

        # ===== 新增子账号ID输入框 =====
        self.account_frame = tk.Frame(content_frame)
        self.account_frame.pack(fill="x", pady=5)

        tk.Label(self.account_frame, text="子账号ID:").pack(side="left")
        # 从历史中加载子账号ID
        last_account_id = self.load_last_input_account_id()
        self.account_entry = tk.Entry(self.account_frame, width=35)
        self.account_entry.pack(side="left", padx=5)
        self.account_entry.insert(0, last_account_id)  # 设置默认值

        # ===== 文件选择框 =====
        self.file_frame = tk.Frame(content_frame)
        self.file_frame.pack(fill="x", pady=5)

        tk.Label(self.file_frame, text="选择文件:").pack(side="left")
        self.file_entry = tk.Entry(self.file_frame, width=50)
        self.file_entry.pack(side="left", padx=5)

        self.browse_btn = tk.Button(self.file_frame, text="选择文件", command=self.select_file)
        self.browse_btn.pack(side="left")

        # 并发数量输入框
        self.concurrency_frame = tk.Frame(content_frame)
        self.concurrency_frame.pack(fill="x", pady=5)

        tk.Label(self.concurrency_frame, text="并发数量:").pack(side="left")
        self.concurrency_entry = tk.Entry(self.concurrency_frame, width=10)
        self.concurrency_entry.pack(side="left", padx=5)
        self.concurrency_entry.insert(0, "3")  # 默认值

        # 启动按钮
        self.start_btn = tk.Button(content_frame, text="开始执行", command=self.start_processing)
        self.start_btn.pack(pady=10)

        # 日志信息显示区域
        tk.Label(content_frame, text="操作日志:").pack(anchor="w")
        self.log_area = scrolledtext.ScrolledText(content_frame, height=10, width=70)
        self.log_area.pack(fill="both", expand=True)

        # 默认禁用日志编辑
        self.log_area.config(state="disabled")

        # ===== Template Section =====
        # 添加打开HTML文件的按钮
        html_btn_frame = tk.Frame(content_frame)
        html_btn_frame.pack(side="left", anchor="sw", pady=10)

        self.open_html_btn = tk.Button(
            html_btn_frame,
            text="辅助生成Excel",
            command=lambda: self.prepare_and_open_html()
        )
        self.open_html_btn.pack(side="left", padx=5)

        # 模板按钮放在左下角
        template_frame = tk.Frame(content_frame)
        template_frame.pack(side="left", anchor="sw", pady=10)

        tk.Button(template_frame, text="📝 信息流模板文件", command=lambda: self.open_template_file(template_name="新界面-种草-信息视频流-样例.xlsx")).pack(side="left", padx=5)
        tk.Button(template_frame, text="📝 搜索模板文件", command=lambda: self.open_template_file(template_name="新界面-种草-搜索推广-样例.xlsx")).pack(side="left", padx=5)

        # 创建按钮和红点的容器
        btn_frame = tk.Frame(content_frame)
        btn_frame.pack(side="right", anchor="se", pady=10)

        # 创建按钮
        self.update_btn = tk.Button(btn_frame, text="检测更新", command=self.start_update_check)
        self.update_btn.pack(side="left")

        # 创建红点（默认不显示）
        self.red_dot = tk.Label(
            btn_frame,
            text="●",
            fg="red",
            bg=btn_frame.cget("bg"),  # 保持背景一致
            font=("Arial", 12)
        )
        self.red_dot.pack(side="left", padx=(5, 0))

        # 底部框架 - 专门放进度条和状态
        bottom_frame = tk.Frame(main_frame)
        bottom_frame.pack(fill="x", pady=10)

        # 下载进度条和状态 - 居中显示
        self.progress = ttk.Progressbar(
            bottom_frame,
            orient="horizontal",
            length=300,
            mode="determinate"
        )

        self.status_var = tk.StringVar()
        status_label = ttk.Label(
            bottom_frame,
            textvariable=self.status_var,
            foreground="blue"
        )
        status_label.pack(pady=5)

    def on_closing(self):
        """窗口关闭前保存当前输入"""
        self.save_account_id_to_json()
        self.master.destroy()

    def monitor_selected_file(self):
        """监控JSON中的 selected_file 字段"""
        json_path = self.get_resource_path(save_base_data_json)
        last_file_path = ""

        while True:
            if os.path.exists(json_path):
                try:
                    with open(json_path, 'r', encoding='utf-8') as f:
                        data = json.load(f)
                        file_path = data.get("selected_file", "")
                        chrome_default_download_path = data.get("chrome_default_download_path", "")
                        final_show_path = rf'{chrome_default_download_path}/{file_path}'

                        if file_path and file_path != last_file_path:
                            # 使用 after 调度到主线程更新UI
                            self.file_entry.after(0, lambda p=final_show_path: self.update_file_entry(p))
                            last_file_path = file_path
                except Exception as e:
                    print("读取文件路径失败: {e}")

            time.sleep(2)  # 每2秒检查一次

    def update_file_entry(self, path):
        """安全更新输入框"""
        self.file_entry.delete(0, tk.END)
        self.file_entry.insert(0, path)
        self.log(f"已自动填充文件路径: {path}", "purple")

    def save_account_id_to_json(self):
        data = {}
        file_name = self.get_resource_path(save_base_data_json)
        # 如果文件存在，读取已有数据
        if os.path.exists(file_name):
            with open(file_name, 'r', encoding='utf-8') as f:
                try:
                    data = json.load(f)
                except json.JSONDecodeError:
                    data = {}

        account_id = self.account_entry.get().strip()
        update_data = {
            "account_id": account_id
        }

        if 'chrome_default_download_path' not in data:
            download_dir = get_chrome_download_directory()
            update_data['chrome_default_download_path'] = download_dir

        self.update_json_data(file_name=file_name, original_data=data, update_data=update_data)

    def prepare_and_open_html(self):
        account_id = self.account_entry.get().strip()
        if not account_id:
            self.log("子账号ID不能为空", "red")
            return

        self.save_account_id_to_json()

        # 1. 运行爬虫获取数据
        thread = threading.Thread(
            target=self.run_spider_to_get_data,
            args=(account_id, html_file)
        )
        thread.start()

    def load_valid_cookie(self, file_name=save_base_data_json):
        """
        从缓存加载有效的 cookie，如果不存在或已过期则返回 None
        """
        if not os.path.exists(file_name):
            self.log("缓存文件不存在", "red")
            return None

        try:
            with open(file_name, 'r', encoding='utf-8') as f:
                cache_data = json.load(f)

            if "expires_at" not in cache_data or "cookie" not in cache_data:
                self.log("⚠️ 缓存数据不完整，忽略缓存", "orange")
                return None

            expires_at = datetime.fromisoformat(cache_data["expires_at"])
            if datetime.now() < expires_at:
                self.log("使用缓存的 Cookie", "purple")
                return cache_data["cookie"]
            else:
                self.log("⏰ Cookie 已过期，需重新登录获取", "orange")
                return None

        except json.JSONDecodeError:
            self.log("❌ JSON 解析失败，缓存文件可能损坏", "red")
            return None
        except Exception as e:
            self.log(f"❌ 加载缓存出错: {e}", "red")
            return None

    def save_cookie_with_expiry(self, cookie_dict, expires_in_minutes=60, file_name=None):
        """
        将 cookie 和过期时间更新到现有 JSON 文件中，不覆盖其他字段
        :param file_name: 文件路径
        :param cookie_dict: 要保存的 cookie 字典
        :param expires_in_minutes: 过期时间（分钟）
        """
        # 构造要更新的内容
        new_data = {
            "cookie": cookie_dict,
            "expires_at": (datetime.now() + timedelta(minutes=expires_in_minutes)).isoformat()
        }

        try:
            # 如果文件存在，读取已有数据
            if os.path.exists(file_name):
                with open(file_name, 'r', encoding='utf-8') as f:
                    try:
                        data = json.load(f)
                    except json.JSONDecodeError:
                        data = {}
            else:
                data = {}

            # 更新指定字段，保留原有其他字段
            data.update(new_data)

            # 写回文件
            with open(file_name, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)

            self.log("✅ Cookie 已缓存, 缓存时间24小时", "purple")
        except Exception as e:
            self.log(f"❌ 缓存Cookie失败: {e}")

    def load_last_input_account_id(self, file_name=save_base_data_json):
        """加载上次的输入"""
        file_name = self.get_resource_path(file_name)

        if os.path.exists(file_name):
            try:
                with open(file_name, "r", encoding="utf-8") as f:
                    data = json.load(f)

                # 检查必要字段是否存在
                if "account_id" not in data:
                    return ''

                return data.get("account_id", "")
            except Exception as e:
                print(f"❌ 加载历史失败: {e}")
        return ""


    def update_json_data(self, file_name: str = None, original_data=None, update_data=None):
        """
        将 cookie 和过期时间更新到现有 JSON 文件中，不覆盖其他字段
        :param original_data:  原始数据
        :param update_data: 要新增修改的数据
        :param file_name: 文件路径
        :param cookie_dict: 要保存的 cookie 字典
        :param expires_in_minutes: 过期时间（分钟）
        """
        # 构造要更新的内容
        if update_data is None:
            update_data = {}

        try:
            # 更新指定字段，保留原有其他字段
            original_data.update(update_data)
            # 写回文件
            with open(file_name, 'w', encoding='utf-8') as f:
                json.dump(original_data, f, ensure_ascii=False, indent=2)
        except Exception as e:
            self.log("保存上一次的子账号ID失败", "red")

    def get_cookie(self, account_id=None, file_name=save_base_data_json):
        file_name = self.get_resource_path(file_name)
        # 先尝试读取缓存中的 cookie
        cached_cookie = self.load_valid_cookie(file_name=file_name)
        if cached_cookie:
            return cached_cookie

        # 缓存无效或不存在，重新登录获取
        unit_dict_cookie = open_jg_page(account_id)

        if unit_dict_cookie:
            # 保存新的 cookie 到缓存，假设有效期为 一天
            self.save_cookie_with_expiry(unit_dict_cookie, expires_in_minutes=60 * 24, file_name=file_name)
            return unit_dict_cookie
        else:
            self.log("获取 cookie 失败", "red")
            return None

    def run_spider_to_get_data(self, account_id=None, html_file=None):
        json_file = save_base_data_json
        spu_name_type = "spuNameList"

        self.log(f'正在获取cookie, 首次获取需要登录聚光后台, 请稍候...', "orange")
        unit_dict_cookie = self.get_cookie(account_id=account_id)

        # spu_name_list = collect_recom_spu_list_exists(cookies=unit_dict_cookie, account_id=account_id)  # 获取spu名称列表数据
        #
        # if spu_name_list:
        #     self.write_target_packages(spu_name_list, json_file, spu_name_type)

        self.open_html_file(html_file)
        pass

    def write_target_packages(self, new_packages: list, json_file: str = save_base_data_json, json_data_type: str = "targetPackages"):
        """
        仅更新指定的 json_data_type 字段的值，保留其他字段不变
        :param new_packages: 新的定向包名称列表
        :param json_file: JSON 文件路径
        :param json_data_type: 要写入的字典key值
        """
        try:
            json_file = self.get_resource_path(json_file)

            # 读取原有数据
            if os.path.exists(json_file):
                with open(json_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
            else:
                data = {}

            # 更新指定字段
            data[json_data_type] = new_packages

            # 写回文件
            with open(json_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)

        except Exception as e:
            print(f"写入JSON文件失败: {e}")
            raise

    # def monitor_json_file(self, json_file, timeout=10, check_interval=0.5, json_data_type=None):
    #     json_file = self.get_resource_path(json_file)
    #     """监控JSON文件直到数据就绪"""
    #     start_time = time.time()
    #
    #     while time.time() - start_time < timeout:
    #         if os.path.exists(json_file):
    #             try:
    #                 with open(json_file, "r", encoding="utf-8") as f:
    #                     data = json.load(f)
    #                     if data.get(json_data_type):
    #                         return data
    #             except (json.JSONDecodeError, IOError):
    #                 pass
    #
    #         time.sleep(check_interval)
    #
    #     return None
    def get_resource_path(self, file_path):
        """获取资源的绝对路径，兼容开发环境和PyInstaller打包环境"""
        if hasattr(sys, '_MEIPASS'):
            # PyInstaller打包后的临时目录
            base_path = sys._MEIPASS
        else:
            # 开发环境下的项目根目录
            base_path = os.path.abspath(".")

        return os.path.join(base_path, file_path)

    def open_html_file(self, file_path=None):
        """用默认浏览器打开本地HTML文件（兼容打包环境）"""
        try:
            # 获取资源路径（兼容打包和开发环境）
            if getattr(sys, 'frozen', False):
                # 打包后：从临时目录或 exe 同级目录查找
                base_path = getattr(sys, '_MEIPASS', os.path.dirname(sys.executable))
            else:
                # 开发时：从脚本目录查找
                base_path = os.path.dirname(os.path.abspath(__file__))

            # 尝试从多个可能的位置查找文件
            possible_paths = [
                os.path.join(base_path, file_path),  # 直接路径
                os.path.join(base_path, "shared", file_path)  # 共享目录（如果有）
            ]

            abs_path = None
            for path in possible_paths:
                if os.path.exists(path):
                    abs_path = path
                    break

            if not abs_path:
                self.log(f"错误: 文件 {file_path} 不存在（搜索路径: {possible_paths}）", "red")
                return

            # 标准化路径并打开
            abs_path = os.path.normpath(abs_path).replace('\\', '/')
            uri = f"file:///{abs_path}" if os.name == 'nt' else f"file://{abs_path}"
            webbrowser.open_new_tab(uri)

        except Exception as e:
            self.log(f"打开HTML文件失败: {str(e)}", "red")

    def safe_log(self, message, color="black"):
        """线程安全的日志方法（通过Tkinter的after调度到主线程）"""
        if self.log_area.winfo_exists():  # 检查控件是否还存在
            self.log_area.after(0, lambda: self.log(message, color))

    def start_update_check(self):
        self.update_btn.config(state="disabled")
        self.status_var.set("正在检查更新...")
        self.log("开始检查更新")
        threading.Thread(target=self.check_update, daemon=True).start()

    def check_update(self):
        try:
            response = requests.get(update_url, timeout=10)
            response.raise_for_status()
            update_info = response.json()

            if self.is_new_version(update_info['version']):
                self.master.after(100, lambda: self.prompt_update(update_info))
            else:
                self.master.after(100, self.show_no_update)

        except Exception as e:
            error_msg = f"检查更新失败: {str(e)}"
            self.master.after(100, lambda: self.show_error(error_msg))

    def check_update_init(self):
        try:
            response = requests.get(update_url, timeout=10)
            response.raise_for_status()
            update_info = response.json()

            if self.is_new_version(update_info['version']):
                # 有新版本，显示红点
                self.red_dot_visible = True
                self.red_dot.pack()  # 显示红点
            else:
                # 没有更新，隐藏红点
                self.red_dot_visible = False
                self.red_dot.pack_forget()  # 隐藏红点

        except Exception as e:
            error_msg = f"检查更新失败: {str(e)}"
            self.master.after(100, lambda: self.show_error(error_msg))

    def show_error(self, message):
        self.log(f"更新错误: {message}", "red")
        self.reset_ui()

    def show_no_update(self):
        message = "当前已是最新版本"
        self.log(message)
        messagebox.showinfo("无更新", message, parent=self.master)
        self.reset_ui()

    def update_progress(self, value, downloaded, total_size):
        self.progress["value"] = value
        self.status_var.set(f"下载中... {value:.1f}% " f"({downloaded // 1024}KB/{total_size // 1024}KB)")

    def is_valid_exe(self, file_path):
        """检查文件是否是有效的Windows可执行文件"""
        try:
            # 检查文件大小
            if os.path.getsize(file_path) < 1024:  # 小于1KB的文件不太可能是有效的EXE
                return False
            # 检查文件头
            with open(file_path, 'rb') as f:
                header = f.read(2)
                if header != b'MZ':  # EXE文件的魔数
                    return False

            return True
        except:
            return False

    def download_file(self, exe_url):
        try:
            # 从URL提取文件名
            file_name = exe_url.split('/')[-1].split('?')[0]  # 移除查询参数
            temp_exe = os.path.join(self.temp_dir, file_name)
            self.log(f"下载文件保存到: {temp_exe}")

            response = requests.get(exe_url, stream=True, timeout=30)
            response.raise_for_status()

            total_size = int(response.headers.get('content-length', 0))
            downloaded = 0

            with open(temp_exe, 'wb') as f:
                for chunk in response.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)
                        downloaded += len(chunk)
                        progress = (downloaded / total_size) * 100 if total_size > 0 else 0
                        self.master.after(10, lambda v=progress: self.update_progress(v, downloaded, total_size))

            # 验证文件完整性
            if 0 < total_size != downloaded:
                error_msg = f"下载文件不完整: {downloaded}/{total_size} bytes"
                self.log(error_msg)
                raise Exception(error_msg)

            # 验证是否是有效的EXE文件
            if not self.is_valid_exe(temp_exe):
                error_msg = "下载的文件不是有效的Windows可执行文件"
                self.log(error_msg)
                raise Exception(error_msg)

            self.log(f"下载完成, 文件大小: {os.path.getsize(temp_exe)} 字节", "green")
            self.master.after(100, lambda: self.prepare_replace(temp_exe))

        except Exception as e:
            error_msg = f"下载失败: {str(e)}"
            self.log(error_msg)
            self.master.after(100, lambda: self.show_error(error_msg))

    def create_replace_script(self, temp_exe_path):
        bat_path = os.path.join(self.temp_dir, "replace.bat")
        current_exe = os.path.realpath(sys.executable)  # 确保指向真实路径

        current_pid = os.getpid()
        self.log(f"当前PID: {current_pid}, EXE: {current_exe}")

        def escape_path(path):
            return path.replace('"', '""').replace('&', '^&').replace('|', '^|')

        current_exe_escaped = escape_path(current_exe)
        temp_exe_path_escaped = escape_path(temp_exe_path)
        temp_dir_escaped = escape_path(self.temp_dir)
        log_file = escape_path(os.path.join(self.temp_dir, "replace_log.txt"))

        script = f"""
                @echo off
                chcp 65001 >nul
                setlocal enabledelayedexpansion

                :: 请求管理员权限
                net session >nul 2>&1
                if %errorLevel% neq 0 (
                    echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\\getadmin.vbs"
                    echo UAC.ShellExecute "%~s0", "", "", "runas", 1 >> "%temp%\\getadmin.vbs"
                    "%temp%\\getadmin.vbs"
                    exit /b
                )

                :: 关闭当前进程
                taskkill /f /pid {current_pid} >nul 2>&1

                :: 等待进程完全退出
                :waitloop
                timeout /t 5 /nobreak >nul

                :: 获取原文件名和备份文件名
                set CURRENT_EXE="{current_exe_escaped}"
                set BACKUP_EXE="{os.path.splitext(current_exe_escaped)[0]}_backup{os.path.splitext(current_exe_escaped)[1]}"

                :: 删除旧的备份文件（如有）
                if exist "!BACKUP_EXE!" del /f /q "!BACKUP_EXE!"

                :: 尝试重命名原文件为备份文件
                if exist "!CURRENT_EXE!" (
                    echo 正在尝试重命名原文件为备份文件...
                    ren "!CURRENT_EXE!" "{os.path.splitext(os.path.basename(current_exe_escaped))[0]}_backup{os.path.splitext(current_exe_escaped)[1]}" >nul 2>&1
                    if errorlevel 1 (
                        echo 重命名失败，尝试直接删除原文件...
                        del /f /q "!CURRENT_EXE!" >nul 2>&1
                        if errorlevel 1 (
                            echo 删除原文件失败，更新中止！
                            exit /b 1
                        )
                    )
                )

                :: 复制新文件
                echo 正在复制新文件... >> "{log_file}"
                copy /Y "{temp_exe_path_escaped}" "{current_exe_escaped}" >nul 2>&1
                if errorlevel 1 (
                    echo 复制失败，尝试解除文件锁定... >> "{log_file}"
                    :: 尝试解除锁定
                    takeown /f "{current_exe_escaped}" /r /d y >nul 2>&1
                    icacls "{current_exe_escaped}" /grant administrators:F /t >nul 2>&1
                    :: 再次尝试复制
                    copy /Y "{temp_exe_path_escaped}" "{current_exe_escaped}" >> "{log_file}" 2>&1
                    if errorlevel 1 (
                        echo 最终复制失败，错误详情： >> "{log_file}"
                        type "{log_file}"
                        exit /b 1
                    )
                )
                
                timeout /t 2 /nobreak >nul
                :: 启动新程序
                start "" "{current_exe_escaped}"

                :: 自清理
                if exist "!BACKUP_EXE!" del /f /q "!BACKUP_EXE!"
                if exist "%temp%\\getadmin.vbs" del /f /q "%temp%\\getadmin.vbs"
                if exist "{temp_dir_escaped}" rd /s /q "{temp_dir_escaped}" >nul 2>&1
                del "%~f0" >nul 2>&1
                """

        with open(bat_path, 'w', encoding='utf-8-sig') as f:
            f.write(script)

        return bat_path

    def execute_replace_script(self, bat_script):
        """执行替换脚本并退出"""
        try:
            self.log(f"正在执行批处理脚本: {bat_script}")

            # 隐藏命令行窗口
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = 0

            # 使用绝对路径执行批处理
            subprocess.Popen(
                ['cmd.exe', '/C', bat_script],
                shell=False,
                startupinfo=startupinfo,
                creationflags=subprocess.CREATE_NO_WINDOW
            )

            # 给脚本时间启动
            time.sleep(3)
            self.log("退出应用程序以完成更新", "purple")
            sys.exit(0)

        except Exception as e:
            error_msg = f"执行更新脚本失败: {str(e)}"
            self.log(error_msg)
            self.show_error(error_msg)

    def prepare_replace(self, temp_exe):
        self.status_var.set("准备安装更新...")
        self.progress["value"] = 100
        self.log(f"准备替换文件: {temp_exe}")

        bat_script = self.create_replace_script(temp_exe)

        if messagebox.askyesno(
                "更新准备就绪",
                "更新已下载完成，需要重启应用,是否立即重启?",
                parent=self.master
        ):
            self.execute_replace_script(bat_script)

    def start_download(self, exe_url):
        self.progress.pack(pady=10)
        self.status_var.set("正在下载更新...")
        self.progress["value"] = 0
        self.log(f"开始下载更新文件...", "green")
        threading.Thread(target=self.download_file, args=(exe_url,), daemon=True).start()

    def prompt_update(self, update_info):
        answer = messagebox.askyesno(
            "发现新版本",
            f"发现新版本 {update_info['version']}\n\n更新内容:\n{update_info.get('changelog', '')}\n\n是否立即更新?",
            parent=self.master
        )

        if answer:
            self.start_download(update_info['exe_url'])
        else:
            self.reset_ui()

    def is_new_version(self, remote_version):
        result = remote_version != current_version
        self.log(f"版本比较: 当前={current_version}, 最新版本={remote_version}", "purple")
        return result

    def reset_ui(self):
        self.progress.pack_forget()
        self.progress["value"] = 0
        self.status_var.set("")
        self.update_btn.config(state="normal")

    def cleanup(self):
        try:
            if os.path.exists(self.temp_dir):
                shutil.rmtree(self.temp_dir, ignore_errors=True)
        except Exception as e:
            self.log(f"清理失败: {str(e)}", "red")

    def open_template_file(self, template_name='种草-信息视频流--创建计划-样例.xlsx'):
        """点击按钮后打开模板 Excel 文件"""
        # 构建模板路径
        if getattr(sys, 'frozen', False):  # 如果是打包后的程序
            base_path = sys._MEIPASS
            full_path = os.path.join(base_path, "shared", template_name)
        else:  # 开发环境
            full_path = os.path.abspath(os.path.join("..", "shared", template_name))

        # 检查文件是否存在
        if not os.path.exists(full_path):
            self.log(f"错误: 未找到模板文件 {full_path}", "red")
            return

        # 使用默认程序打开 Excel 文件
        try:
            if os.name == 'nt':  # Windows
                os.startfile(full_path)
            else:  # macOS / Linux
                import subprocess
                if sys.platform == 'darwin':
                    subprocess.call(['open', full_path])  # macOS
                else:
                    subprocess.call(['xdg-open', full_path])  # Linux
        except Exception as e:
            self.log(f"❌ 打开模板文件失败: {str(e)}", "red")

    def select_file(self):
        """打开文件选择对话框"""
        file_path = filedialog.askopenfilename(
            title="选择文件",
            filetypes=[("Excel文件", "*.xlsx")]
        )
        if file_path:
            self.file_path = file_path
            self.file_entry.delete(0, tk.END)
            self.file_entry.insert(0, file_path)
            self.log(f"已选择文件: {file_path}")

    def log(self, message, color="black"):
        """向日志区域添加消息"""
        self.log_area.config(state="normal")

        # 创建或复用标签
        tag_name = f"color_{color}"
        self.log_area.tag_configure(tag_name, foreground=color)

        self.log_area.insert(tk.END, message + "\n", tag_name)
        self.log_area.config(state="disabled")
        self.log_area.see(tk.END)  # 自动滚动到底部

    def start_processing(self):
        """启动处理按钮的回调函数"""
        # 获取用户输入
        self.file_path = self.file_entry.get()
        account_id = self.account_entry.get().strip()

        try:
            self.concurrency = int(self.concurrency_entry.get())
            if self.concurrency <= 0:
                raise ValueError("并发数必须大于0")

            if not account_id:
                self.log("子账号ID不能为空", "red")
                return

        except ValueError as e:
            self.log(f"错误: 并发数量必须是正整数 ({e})")
            return
        if not self.file_path:
            self.log("错误: 请先选择文件")
            return

        self.log(f"开始处理 - 文件: {self.file_path}, 并发数: {self.concurrency}")

        # start_sub_account_url(self.file_path, self.concurrency)
        thread = threading.Thread(
            target=start_sub_account_url,
            args=(self.file_path, self.concurrency, account_id),
            kwargs={'log_callback': self.log}  # 传递前端方法
        )
        thread.start()

        # 这里调用你的处理函数，例如:
        # process_file(self.file_path, self.concurrency)
        # 为了演示，我们模拟一个处理过程
        # self.simulate_processing()


if __name__ == "__main__":
    root = tk.Tk()
    flask_thread = threading.Thread(target=start_server, daemon=True)
    flask_thread.start()
    app = Application(master=root)
    app.mainloop()
